/*
 * Created by Jaren at 2022/2/7 13:43
 */
import 'dart:async';
import 'dart:typed_data';

import 'package:flutter/foundation.dart';
import 'package:inmotion_protocol/base/file/file_manager.dart';
import 'package:inmotion_protocol/base/protocol.dart';
import 'package:inmotion_protocol/common/exceptions.dart';
import 'package:inmotion_protocol/common/fw_update_manager.dart';
import 'package:inmotion_protocol/common/mode.dart';
import 'package:inmotion_protocol/common/response.dart';
import 'package:inmotion_protocol/features.dart';
import 'package:inmotion_protocol/handler/received_handler.dart';
import 'package:inmotion_protocol/handler/send_handler.dart';
import 'package:inmotion_protocol/inmotion_protocol.dart';
import 'package:inmotion_protocol/lorin/bean/common/date.dart';
import 'package:inmotion_protocol/lorin/constant.dart';
import 'package:inmotion_protocol/lorin/file/lorin_file_manager.dart';
import 'package:inmotion_protocol/lorin/file/lorin_file_upload_manager.dart';
import 'package:inmotion_protocol/lorin/lorin_message.dart';
import 'package:inmotion_protocol/protocol_worker.dart';
import 'package:inmotion_protocol/util/byte_util.dart';
import 'package:inmotion_protocol/util/device.dart';
import 'package:inmotion_protocol/util/log.dart';
import 'package:jieli_ble_ota_plugin/jieli_ble_ota_plugin.dart';

class LorinFwUpdateManager extends FwUpdateManager {
  /// 以强制固件升级中，等级15、最小最大限制版本号255.255.65534作为正在升级标识
  static const markAsUpdatingLevel = 15;

  /// 以强制固件升级中，等级15、最小最大限制版本号255.255.65534作为正在升级标识
  static const markAsUpdatingVersion = "255.255.65534";

  final Protocol<LorinMessage> _protocol;
  _SingleDeviceUpdateManager? _singleDeviceUpdateManager;

  final MultiDeviceUpdateConfig _config = MultiDeviceUpdateConfig();

  final LinkLayerDeviceHandler? _linkLayerDeviceHandler;

  RequiredManualOperationHandler? requiredManualOperationHandler;

  StreamController<FwUpdateEvent> multiUpdateStreamController =
      StreamController();

  bool _continueUpdate = true;

  LorinFwUpdateManager(this._protocol, this._linkLayerDeviceHandler,
      this.requiredManualOperationHandler);

  @override
  void cancel() {
    _continueUpdate = false;
    if (_singleDeviceUpdateManager != null)
      _singleDeviceUpdateManager!.cancel();
  }

  @override
  void config(MultiDeviceUpdateConfig config) {
    _config.singleConfigs.clear();
    _config.singleConfigs.addAll(config.singleConfigs);
    _config.defaultCommDevice = config.defaultCommDevice;
    _config.supportMarkAsUpdating = config.supportMarkAsUpdating;
    _config.deviceRequestingUpdate = config.deviceRequestingUpdate;
  }

  @override
  void destroy() {
    if (!multiUpdateStreamController.isClosed)
      multiUpdateStreamController.close();
    if (_singleDeviceUpdateManager != null)
      _singleDeviceUpdateManager!.destroy();
  }

  @override
  Stream<FwUpdateEvent> start() {
    _continueUpdate = true;

    if (multiUpdateStreamController.isClosed)
      multiUpdateStreamController = StreamController();

    int deviceCountNeedUpdate = _config.singleConfigs.length;
    List<int> configKeys = _config.singleConfigs.keys.toList(growable: false);

    int currentIndexRetryTimes = 0;
    start0(int index) {
      SingleDeviceUpdateConfig singleConfig =
          _config.singleConfigs[configKeys[index]]!;
      if (_config.deviceRequestingUpdate != null &&
          singleConfig.device == _config.deviceRequestingUpdate) {
        singleConfig.deviceRequestingUpdate = true;
      }
      _singleDeviceUpdateManager =
          _getSingleFwUpdateManager(singleConfig.device);
      _singleDeviceUpdateManager!.config(index, _linkLayerDeviceHandler,
          requiredManualOperationHandler, singleConfig);
      _singleDeviceUpdateManager!.setFileManagerHook(index == 0
          ? MarkAsUpgradingFileHook(singleConfig.device, _protocol)
          : null);

      _singleDeviceUpdateManager!.start().listen((event) async {
        if (!_continueUpdate) {
          // 升级中断，不再往stream中添加事件
          return;
        }
        if (event.step != FwUpdateEvent.stepSuccess) {
          multiUpdateStreamController.add(event);
          return;
        }
        if (index + 1 >= deviceCountNeedUpdate) {
          // 全部设备升级完成

          // 清除升级标识
          int clearResult = await clearUpdatingMark();
          if (clearResult != 0) {
            multiUpdateStreamController.addError(
                FwUpdateError.byClearUpdatingMark(
                    singleConfig.device, clearResult));
            return;
          }
          multiUpdateStreamController.add(event);
          return;
        }

        // 当前设备升级成功，继续下个设备
        start0(index + 1);

        /// 只在最后一个设备升级时，才上报success
      }).onError((error) {
        if (currentIndexRetryTimes == 0 &&
            error is FwUpdateError &&
            error.step == FwUpdateError.stepCheckUpdateSuccess &&
            error.errors.isNotEmpty &&
            error.errors.first == FwUpdateError.errorVersionNotMatch) {
          // 版本号没变，部分车型有BUG，第一次不能完成升级，需重试一次
          currentIndexRetryTimes++;
          start0(index);
          return;
        }
        multiUpdateStreamController.addError(error);
      });
    }

    // Let's go!
    start0(0);
    return multiUpdateStreamController.stream;
  }

  Future<int> clearUpdatingMark() async {
    if (InMotionProtocol.isFeatureSupported(
            Feature.forceFmUpdate,
            _protocol.productProperty.model,
            _protocol.productProperty.versionWrapper) <=
        0) {
      // 不支持升级标识
      return Future.value(0);
    }
    var securityConnection = true;
    if (InMotionProtocol.isFeatureSupported(
            Feature.securityConnection,
            _protocol.productProperty.model,
            _protocol.productProperty.versionWrapper) >
        0) {
      // 支持安全连接的型号需建立安全连接
      securityConnection = await _protocol.buildSecurityConnection();
    }

    if (!securityConnection) {
      // 安全连接失败
      return Future.value(FwUpdateError.errorSecurityConnection);
    }
    return _protocol
        .writeForceUpdate(0, '0.0.0', '0.0.0')
        .then((rsp) => rsp.error.index);
  }

  _SingleDeviceUpdateManager _getSingleFwUpdateManager(int device) {
    if ((_protocol.productProperty.model.isE20Series() ||
            _protocol.productProperty.model.isV6Series()) &&
        device == DeviceUtils.lorinVBtDevice) {
      if (_singleDeviceUpdateManager is _JieliUpdateManager)
        return _singleDeviceUpdateManager!;
      return _JieliUpdateManager(_protocol, this);
    }
    if (_singleDeviceUpdateManager is _StandardSingleDeviceUpdateManager)
      return _singleDeviceUpdateManager!;
    return _StandardSingleDeviceUpdateManager(_protocol, this);
  }
}

abstract class _SingleDeviceUpdateManager {
  Stream<FwUpdateEvent> start();

  void cancel();

  void destroy();

  void config(
      int index,
      LinkLayerDeviceHandler? linkLayerDeviceHandler,
      RequiredManualOperationHandler? requiredManualOperationHandler,
      SingleDeviceUpdateConfig config);

  void setFileManagerHook(ActionHook? hook);
}

/// 使用 Lorin 协议进行固件升级的标准 manager
class _StandardSingleDeviceUpdateManager extends _SingleDeviceUpdateManager {
  final Protocol<LorinMessage> _protocol;
  LorinFileUploadManager? _lorinFileManager;

  SingleDeviceUpdateConfig? _singleDeviceUpdateConfig;
  LinkLayerDeviceHandler? _linkLayerDeviceHandler;

  RequiredManualOperationHandler? requiredManualOperationHandler;

  StreamController<FwUpdateEvent> fwUpdateStreamController = StreamController();

  final LorinFwUpdateManager parent;

  _StandardSingleDeviceUpdateManager(this._protocol, this.parent);

  @override
  void config(
      int index,
      LinkLayerDeviceHandler? linkLayerDeviceHandler,
      RequiredManualOperationHandler? requiredManualOperationHandler,
      SingleDeviceUpdateConfig config) {
    if (_lorinFileManager != null) {
      _lorinFileManager!.interrupt();
      _lorinFileManager!.destroy();
    }
    print("✅✅✅✅ = fileSize = 开始升级  config.device = ${config.device}  ");
    LorinFileManagerConfig fileConfig = LorinFileManagerConfig(
        config.device,
        2,
        config.device == Constant.deviceScreen
            ? config.screenFilename
            : config.remoteFilename,
        config.localFile);
    _lorinFileManager = LorinFileUploadManager(_protocol, fileConfig);
    _singleDeviceUpdateConfig = config;
    _linkLayerDeviceHandler = linkLayerDeviceHandler;
    this.requiredManualOperationHandler = requiredManualOperationHandler;
    if (config.isLinkLayerDevice && config.deviceRequestingUpdate) {
      LogUtils.w(
          "Link layer device is requesting update, reset max size peer pkg to 120.");
      _protocol.maxSizePeerPkg = 120;
    }
  }

  Future<bool> checkModeForUpdate() {
    if (_protocol.productProperty.model.isEuc()) {
      return _protocol.requestRealTimeState().then((state) {
        return state.pcMode != Mode.working;
      });
    }
    return Future.value(true);
  }

  @override
  void setFileManagerHook(ActionHook? hook) {
    _lorinFileManager?.actionHook = hook;
  }

  @override
  Stream<FwUpdateEvent> start() {
    if (_lorinFileManager == null)
      throw NullCheckException(
          'LorinFileManager is null. Please call config() first.');
    if (_singleDeviceUpdateConfig == null)
      throw NullCheckException(
          'SingleDeviceUpdateConfig is null. Please call config() first.');

    if (!fwUpdateStreamController.isClosed) fwUpdateStreamController.close();

    fwUpdateStreamController = StreamController();

    fwUpdateStreamController
        .add(FwUpdateEvent.byPreparing(_singleDeviceUpdateConfig!.index));

    checkModeForUpdate().then((canUpdate) {
      if (!canUpdate) {
        fwUpdateStreamController.addError(
            FwUpdateError.byStartUpdate(_singleDeviceUpdateConfig!.device, 3));
        return;
      }

      Uint8ClampedList? para;
      bool isScreen =
          _singleDeviceUpdateConfig!.device == Constant.deviceScreen;
      // 屏幕升级
      if (isScreen) {
        para = Uint8ClampedList.fromList([0x01]);
      }
      _protocol
          .startUpdate(
        _singleDeviceUpdateConfig!.device,
        para: para,
      )
          .then((notifyRsp) async {
        if (isScreen) {
          print("✅✅✅✅ = 开始屏幕升级 - 发送下载指令");
          // 发送下载指令
          List<int> list = [0xEE, 0xB6];
          list.addAll([0x00, 0x11]);
          list.addAll([0x88, 0x11, 0x00, 0x03, 0x84, 0x00]);
          int fileSize = _lorinFileManager!.localFileBytes().length;
          Uint8List size = Uint8List(4)
            ..buffer.asByteData().setInt32(0, fileSize, Endian.big);
          list.addAll(size);
          list.addAll(ByteUtil.str2Bytes('ota.bin'));
          list.addAll([0xFF, 0xFC, 0xFF, 0xFF]);

          // 开始文件传输
          return _protocol
              .startTransfer(_singleDeviceUpdateConfig!.device,
                  para: Uint8ClampedList.fromList(list))
              .then((value) {
            if (value.isSuccess()) {
              return _lorinFileManager!.transferFileToScreen(
                  _lorinFileManager!.localFileBytes().length);
            }

            /// TODO: 操作错误码
            throw FwUpdateError.byStartUpdate(
                _singleDeviceUpdateConfig!.device, notifyRsp.error.index);
          });
        } else {
          if (notifyRsp.isSuccess()) {
            return _lorinFileManager!.start();
          }

          /// TODO: 操作错误码
          throw FwUpdateError.byStartUpdate(
              _singleDeviceUpdateConfig!.device, notifyRsp.error.index);
        }
      }).then((fileEventStream) {
        fileEventStream.listen((fileEvent) {
          if (fileEvent.type == FileEvent.fileProgress) {
            // 文件传输进度
            fwUpdateStreamController.add(FwUpdateFileTransferringEvent(
                _singleDeviceUpdateConfig!.index,
                fileEvent as FileProgressEvent));
          } else if (fileEvent.type == FileEvent.prepareFileChecking) {
            // 准备文件检验
            fwUpdateStreamController.add(
                FwUpdateEvent.byFileChecking(_singleDeviceUpdateConfig!.index));
          } else if (fileEvent.type == FileEvent.fileChecked) {
            // 文件校验完成，等待设备升级
            fwUpdateStreamController.add(FwUpdateEvent.byWaiting4Update(
                _singleDeviceUpdateConfig!.index));
          } else if (fileEvent.type == FileEvent.fileClosed) {
            // 延时后查询升级是否成功
            parent.waitThenCheckUpdateSuccess(
                _singleDeviceUpdateConfig!.device,
                _protocol,
                fwUpdateStreamController,
                _singleDeviceUpdateConfig!,
                _linkLayerDeviceHandler!,
                requiredManualOperationHandler!);
          } else {
            // 忽略其余文件操作事件
          }
        }).onError((error) {
          if (error is FileOperationStepError) {
            fwUpdateStreamController.addError(FwUpdateError.byFileOperation(
                _singleDeviceUpdateConfig!.device, error));
            return;
          }
          fwUpdateStreamController.addError(error);
        });
      }).catchError((error) {
        if (error is MsgTimeoutException) {
          fwUpdateStreamController.addError(FwUpdateError.byStartUpdate(
              _singleDeviceUpdateConfig!.device, FwUpdateError.errorTimeout));
        } else if (error is FileOperationStepError) {
          // 文件传输过程异常
          fwUpdateStreamController.addError(FwUpdateError.byFileOperation(
              _singleDeviceUpdateConfig!.device, error));
        } else {
          fwUpdateStreamController.addError(error);
        }
      });
    });

    return fwUpdateStreamController.stream;
  }

  @override
  void cancel() {
    if (_lorinFileManager != null) _lorinFileManager!.interrupt();
  }

  @override
  void destroy() {
    if (_lorinFileManager != null) _lorinFileManager!.destroy();
    if (!fwUpdateStreamController.isClosed) fwUpdateStreamController.close();
  }
}

/// 使用杰里协议进行固件升级的 manager
class _JieliUpdateManager extends _SingleDeviceUpdateManager with OtaCallback {
  final Protocol _protocol;
  final LorinFwUpdateManager _parent;

  int maxRetryConnectTimes = 3;

  late final _JieliUpdateManagerLinkLayerHandler _jieliLinkLayerHandler;

  late JieliBleOtaPlugin _jieliBleOtaPlugin;

  SingleDeviceUpdateConfig? _singleDeviceUpdateConfig;
  RequiredManualOperationHandler? _requiredManualOperationHandler;

  StreamController<FwUpdateEvent> fwUpdateStreamController = StreamController();

  _JieliUpdateManager(this._protocol, this._parent) {
    _jieliLinkLayerHandler = _JieliUpdateManagerLinkLayerHandler();
    _jieliBleOtaPlugin = JieliBleOtaPlugin(
        _JieliUpdateManagerDataWriteHandler(
            _protocol.protocolWorker().sendHandler),
        this,
        _jieliLinkLayerHandler);
    _jieliLinkLayerHandler._hostPlugin = _jieliBleOtaPlugin;
  }

  @override
  void config(
      int index,
      LinkLayerDeviceHandler? linkLayerDeviceHandler,
      RequiredManualOperationHandler? requiredManualOperationHandler,
      SingleDeviceUpdateConfig config) {
    _jieliLinkLayerHandler._hostLinkLayerDeviceHandler = linkLayerDeviceHandler;
    _requiredManualOperationHandler = requiredManualOperationHandler;
    _singleDeviceUpdateConfig = config;
  }

  var waitingDeviceFirmwareStateResponse4StartUpdate = false;
  Timer? waitingDeviceFirmwareStateResponse4StartUpdateTimeoutTask;

  @override
  Stream<FwUpdateEvent> start() {
    if (kDebugMode) print("${DateTime.now()} 03-22: start update");
    if (_singleDeviceUpdateConfig == null)
      throw NullCheckException(
          'SingleDeviceUpdateConfig is null. Please call config() first.');

    if (!fwUpdateStreamController.isClosed) fwUpdateStreamController.close();

    fwUpdateStreamController = StreamController();

    fwUpdateStreamController
        .add(FwUpdateEvent.byPreparing(_singleDeviceUpdateConfig!.index));

    _jieliLinkLayerHandler.onReconnectFailedTooManyTimesCallback = () {
      if (kDebugMode) print("On reconnect failed too many times.");
      if (!fwUpdateStreamController.isClosed) {
        fwUpdateStreamController.addError(FwUpdateError(
            _singleDeviceUpdateConfig!.device,
            FwUpdateError.stepReconnectAfterBlDownloaded));
      }
    };

    int reconnectRetryTimes = 0;
    bool handleReconnectCallback = false;

    _reconnect() {
      _jieliLinkLayerHandler.reconnect(false, true,
          (connected, deviceIdentifier, deviceName) async {
        print(
            "${DateTime.now()} 03-22: reconnect result 000000000 $handleReconnectCallback");
        if (handleReconnectCallback) return;
        handleReconnectCallback = true;
        if (kDebugMode)
          print("${DateTime.now()} 03-22: reconnect result $connected");
        if (kDebugMode) {
          print(
              "${DateTime.now()} 03-22: connection changed call in start(), connected: $connected, reconnectByPlugin: ${_jieliLinkLayerHandler.requestReconnectByPlugin}, retry times $reconnectRetryTimes");
        }

        if (!connected && !_jieliLinkLayerHandler.requestReconnectByPlugin) {
          if (kDebugMode) print("${DateTime.now()} 03-22: reconnect retry 0");

          /// 升级启动时 重连失败
          if (reconnectRetryTimes < maxRetryConnectTimes) {
            // 等待 1500ms 后重连
            // await Future.delayed(const Duration(milliseconds: 1500));
            if (_jieliLinkLayerHandler.requestReconnectByPlugin) {
              reconnectRetryTimes++;
              print(
                  "${DateTime.now()} 03-22: reconnect retry $reconnectRetryTimes");
            }
            handleReconnectCallback = false;
            return;
          }
          if (kDebugMode)
            print("${DateTime.now()} 03-22: reconnect retry timeout");
          fwUpdateStreamController.addError(FwUpdateError.byStartUpdate(
              _singleDeviceUpdateConfig!.device, FwUpdateError.errorTimeout));
          handleReconnectCallback = false;
          return;
        }
        if (connected) {
          if (kDebugMode) print("${DateTime.now()} 03-22: reconnect 0");
          waitingDeviceFirmwareStateResponse4StartUpdate = true;
          _jieliBleOtaPlugin.notifyLinkConnected(deviceIdentifier, deviceName);
          // 等待设备认证等操作
          await Future.delayed(const Duration(milliseconds: 2000));
          if (kDebugMode) print("${DateTime.now()} 03-22: reconnect 1");

          _jieliBleOtaPlugin.requestDeviceFirmwareState();
          waitingDeviceFirmwareStateResponse4StartUpdate = true;

          if (kDebugMode) print("${DateTime.now()} 03-22: reconnect 2");
          waitingDeviceFirmwareStateResponse4StartUpdateTimeoutTask =
              Timer(const Duration(milliseconds: 5000), () {
            if (kDebugMode) print("${DateTime.now()} 03-22: reconnect 3");
            fwUpdateStreamController.addError(FwUpdateError.byStartUpdate(
                _singleDeviceUpdateConfig!.device, FwUpdateError.errorTimeout));
            waitingDeviceFirmwareStateResponse4StartUpdateTimeoutTask = null;
            waitingDeviceFirmwareStateResponse4StartUpdate = false;
            // 防止被杰理SDK连接，未断开
            breakJieLiBLE();
          });

          handleReconnectCallback = false;
        }
      });
    }

    breakJieLiBLE();
    _protocol.startUpdate(_singleDeviceUpdateConfig!.device).then((notifyRsp) {
      breakJieLiBLE(state: false);
      if (notifyRsp.isSuccess()) {
        // 断开重连，以使用可能存在的新的配置
        _jieliLinkLayerHandler.disconnect();
        Future.delayed(const Duration(milliseconds: 1500), () {
          if (kDebugMode) print("${DateTime.now()} 03-22: reconnect 0");
          _reconnect();
        });
      } else {
        throw FwUpdateError.byStartUpdate(
            _singleDeviceUpdateConfig!.device, notifyRsp.error.index);
      }
    }).catchError((error) {
      if (kDebugMode) print("${DateTime.now()} 03-22: startUpdate err: $error");
      if (error is MsgTimeoutException) {
        fwUpdateStreamController.addError(FwUpdateError.byStartUpdate(
            _singleDeviceUpdateConfig!.device, FwUpdateError.errorTimeout));
      } else if (error is FileOperationStepError) {
        // 文件传输过程异常
        fwUpdateStreamController.addError(FwUpdateError.byFileOperation(
            _singleDeviceUpdateConfig!.device, error));
      } else {
        fwUpdateStreamController.addError(error);
      }
    });
    return fwUpdateStreamController.stream;
  }

  void breakJieLiBLE({
    bool state = true,
  }) {
    if (state) {
      _protocol.protocolWorker().dataReceiverInterruptor = null;
      _jieliLinkLayerHandler._hostLinkLayerDeviceHandler!
          .updateLinkConfig(_jieliLinkLayerHandler.genLinkLayerConfig(false));
    } else {
      _protocol.protocolWorker().dataReceiverInterruptor =
          _JieliUpdateManagerDataReceivedHandler(_jieliBleOtaPlugin);
      _jieliLinkLayerHandler._hostLinkLayerDeviceHandler!
          .updateLinkConfig(_jieliLinkLayerHandler.genLinkLayerConfig(true));
    }
  }

  @override
  void destroy() {}

  @override
  void setFileManagerHook(ActionHook? hook) {}

  @override
  void cancel() {
    if (kDebugMode) print("Don't support to cancel.");
  }

  @override
  void onDeviceFirmwareStatusRequested(
      bool success, bool isFirmwareBroken) async {
    if (kDebugMode)
      print("${DateTime.now()} 03-22: onDeviceFirmwareStatusRequested 0");
    if (waitingDeviceFirmwareStateResponse4StartUpdate) {
      if (kDebugMode)
        print("${DateTime.now()} 03-22: onDeviceFirmwareStatusRequested 1");
      waitingDeviceFirmwareStateResponse4StartUpdate = false;

      if (waitingDeviceFirmwareStateResponse4StartUpdateTimeoutTask != null) {
        if (kDebugMode)
          print("${DateTime.now()} 03-22: onDeviceFirmwareStatusRequested 3");
        waitingDeviceFirmwareStateResponse4StartUpdateTimeoutTask!.cancel();
        waitingDeviceFirmwareStateResponse4StartUpdateTimeoutTask = null;
      }

      // 等待设备认证等操作
      await Future.delayed(const Duration(milliseconds: 2000));

      if (kDebugMode)
        print("${DateTime.now()} 03-22: onDeviceFirmwareStatusRequested 2");
      // 继续升级
      _jieliBleOtaPlugin.start(_singleDeviceUpdateConfig!.localFile.path);
    }
  }

  @override
  void onOtaProgressChanged(int type, double progress) {
    print(
        "${DateTime.now()} 03-22: onOtaProgressChanged: $type, progress: $progress");
    if (type == JieliBleOtaPlugin.updateTypeBootloader) {
      // 正在下载 bootloader，即认为在 preparing
      fwUpdateStreamController.add(FwUpdatePreparingEvent.create(
          _singleDeviceUpdateConfig!.index, (progress * 100).toInt(), 10000));
    } else if (type == JieliBleOtaPlugin.updateTypeApp) {
      fwUpdateStreamController.add(FwUpdateFileTransferringEvent(
          _singleDeviceUpdateConfig!.index,
          FileProgressEvent((progress * 100).toInt(), 10000, -1, -1, -1, -1)));
    }
  }

  @override
  void onOtaUpdateFailed(String code) {
    if (kDebugMode) print("${DateTime.now()} 03-22: onOtaUpdateFailed: $code");

    /// 移除相关配置，以使用默认的连接配置
    breakJieLiBLE();

    var error = FwUpdateError(
        _singleDeviceUpdateConfig!.index, FwUpdateError.stepFileOperation);
    // code 为类似于 1|2|3 的字符串
    if (code.contains("|")) {
      List<String> codes = code.split("|");
      if (codes.length == 3) {
        for (int i = 0; i < 3; i++) {
          error.errors[i] = int.tryParse(codes[i]) ?? 0;
        }
      } else {
        error.errors[0] = FwUpdateError.errorUnknown;
      }
    } else {
      error.errors[0] = FwUpdateError.errorUnknown;
    }
    fwUpdateStreamController.addError(error);
  }

  @override
  void onOtaUpdateSuccess() async {
    // 重置 interruptor，继续使用 lorin 协议查询版本号判断是否升级成功
    /// 移除相关配置，以使用默认的连接配置
    breakJieLiBLE();
    fwUpdateStreamController
        .add(FwUpdateEvent.byWaiting4Update(_singleDeviceUpdateConfig!.index));
    if (kDebugMode) print("${DateTime.now()} 03-22: onOtaUpdateSuccess");
    // 等待然后重连，并查询版本号检查升级是否成功
    _parent.waitThenCheckUpdateSuccess(
        _singleDeviceUpdateConfig!.device,
        _protocol,
        fwUpdateStreamController,
        _singleDeviceUpdateConfig!,
        _jieliLinkLayerHandler._hostLinkLayerDeviceHandler!,
        _requiredManualOperationHandler!, jeLiOtaCallback: () async {
      if (kDebugMode) print("${DateTime.now()} 03-22: jeLiOtaCallback");
      // 连接成功后，发起 人机版重启指令，查版本号
      await _protocol.requestRebootHmic();
      await Future.delayed(const Duration(milliseconds: 500));
    });
  }
}

class _JieliUpdateManagerDataReceivedHandler extends ReceivedHandler {
  JieliBleOtaPlugin host;

  _JieliUpdateManagerDataReceivedHandler(this.host);

  @override
  void onDataReceived(Uint8ClampedList bytes, [String clientId = '']) {
    host.notifyDataReceived(Uint8List.fromList(bytes));
  }
}

class _JieliUpdateManagerDataWriteHandler extends DataWriteHandler {
  final SendHandler _hostSendHandler;

  _JieliUpdateManagerDataWriteHandler(this._hostSendHandler);

  @override
  Future writeBytes(Uint8List bytes) {
    return Future(() =>
        _hostSendHandler.send(Uint8ClampedList.fromList(bytes), 'default'));
  }
}

class _JieliUpdateManagerLinkLayerHandler extends LinkLayerHandler {
  JieliBleOtaPlugin? _hostPlugin;
  LinkLayerDeviceHandler? _hostLinkLayerDeviceHandler;

  /// 在 reconnect() 中，
  Function? onReconnectFailedTooManyTimesCallback;

  /// 用于 reconnect() 中连接断开事件最大触发次数
  /// 超过此次数后，即使 reconnect() 时 shouldNotifySDKWhenDisconnected 为 true，也将停止通知 SDK
  int maxDisconnectTriggeredTimes = 3;

  bool requestReconnectByPlugin = false;

  _JieliUpdateManagerLinkLayerHandler();

  @override
  Future reconnect(
      [bool shouldNotifySDKWhenConnected = true,
      bool shouldNotifySDKWhenDisconnected = true,
      Function? connectCallback]) {
    print(
        "${DateTime.now()} 03-22: reconnect, shouldNotifySDKWhenConnected: $shouldNotifySDKWhenConnected, shouldNotifySDKWhenDisconnected: $shouldNotifySDKWhenDisconnected, connectCallback is null: ${connectCallback == null}");
    if (shouldNotifySDKWhenConnected &&
        shouldNotifySDKWhenDisconnected &&
        connectCallback == null) {
      requestReconnectByPlugin = true;
    } else {
      requestReconnectByPlugin = false;
    }

    /// 在重新建立连接时，进行必要的配置更新
    /// 如，杰理 通过 BLE 方式更新时，工作的 service 和 characteristic
    /// 可能与正常通讯的不同，因此需要在连接前对 link layer 进行必要的配置更新
    /// 配置更新需要对应的 link layer 具体实现
    _hostLinkLayerDeviceHandler!.updateLinkConfig(genLinkLayerConfig(true));

    /// 本次 reconnect() 中，disconnect 事件触发次数
    /// 此标识也会在 connect 事件触发 1 次后重置
    int disconnectTriggeredTimes = 0;

    /// 更新配置后，执行连接逻辑
    return Future(() =>
        _hostLinkLayerDeviceHandler!.connect((connected, identifier, name) {
          print(
              "${DateTime.now()} 03-22: override reconnect result $connected, callback: ${connectCallback != null}");
          if (connectCallback != null) {
            connectCallback(connected, identifier, name);
          }
          if (connected) {
            _hostLinkLayerDeviceHandler!
                .updateLinkConfig(genLinkLayerConfig(false));

            /// 重置 disconnected 触发次数
            disconnectTriggeredTimes = 0;

            if (shouldNotifySDKWhenConnected) {
              _hostPlugin!.notifyLinkConnected(identifier, name);
            }
          } else {
            if (disconnectTriggeredTimes > maxDisconnectTriggeredTimes) {
              if (kDebugMode) {
                print(
                    "${DateTime.now()} 03-22: disconnect event is triggered too many times. Stop forwarding to plugin.");
              }
              if (onReconnectFailedTooManyTimesCallback != null) {
                onReconnectFailedTooManyTimesCallback!();
              }
              return;
            }
            disconnectTriggeredTimes++;

            print(
                "${DateTime.now()} 03-22: override reconnect disconnectTriggeredTimes $disconnectTriggeredTimes");
            if (shouldNotifySDKWhenDisconnected) {
              _hostPlugin!.notifyLinkDisconnected(identifier, name);
            }
          }
        }));
  }

  @override
  Future disconnect() {
    if (kDebugMode) print("Request to disconnect.");
    return Future(() => _hostLinkLayerDeviceHandler!.disconnect());
  }

  Map<String, dynamic> genLinkLayerConfig(bool useConfig) {
    Map<String, dynamic> bleUUIDConfig = {};
    bleUUIDConfig['use_config'] = useConfig;
    _hostPlugin!
        .getBleCommunicationUUID()
        .forEach((key, uuid) => bleUUIDConfig[key] = uuid);
    return {'ble_uuid_config': bleUUIDConfig};
  }
}

class MarkAsUpgradingFileHook extends ActionHook {
  final Protocol<LorinMessage> _protocol;
  final int _device;

  MarkAsUpgradingFileHook(this._device, this._protocol);

  @override
  Future<void> run(FileEvent event, Object? data) async {
    if (event.type != FileEvent.prepareFileChecking) return;
    // 文件校验前

    if (InMotionProtocol.isFeatureSupported(
            Feature.forceFmUpdate,
            _protocol.productProperty.model,
            _protocol.productProperty.versionWrapper) <=
        0) {
      // 不支持安全连接
      return;
    }

    var securityConnection = true;
    if (InMotionProtocol.isFeatureSupported(
            Feature.securityConnection,
            _protocol.productProperty.model,
            _protocol.productProperty.versionWrapper) >
        0) {
      // 支持安全连接的型号需建立安全连接
      securityConnection = await _protocol.buildSecurityConnection();
    }
    if (!securityConnection) {
      // 安全连接失败
      throw FwUpdateError.byMarkAsUpdating(
          _device, FwUpdateError.errorSecurityConnection);
    }
    int markResult = await _protocol
        .writeForceUpdate(
            LorinFwUpdateManager.markAsUpdatingLevel,
            LorinFwUpdateManager.markAsUpdatingVersion,
            LorinFwUpdateManager.markAsUpdatingVersion)
        .then((rsp) => rsp.error.index);

    if (markResult == 0) return;
    // 标记失败
    throw FwUpdateError.byMarkAsUpdating(_device, markResult);
  }
}
